#include "ota_version.h"
#include "ota_http.h"
#include "log/log.h"
#include "cJSON/cJSON.h"
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
//需要引入头文件sha加密和重启系统
#include <openssl/sha.h>
#include <sys/reboot.h>


//AI生成的根据文件计算sha值
/**
 * 获取文件的SHA1哈希值（40位16进制字符串）
 * 相同文件返回相同的哈希值，可用于判断文件是否相同
 * linux命令生成：sha1sum 文件名
 * 此函数可以利用AI工具帮我们生成
 */
static char *get_file_sha(char *filepath)
{
    FILE *file = fopen(filepath, "rb");
    if (!file)
    {
        perror("Failed to open file");
        return NULL;
    }

    unsigned char hash[SHA_DIGEST_LENGTH];
    SHA_CTX sha1;
    SHA1_Init(&sha1);

    const int bufSize = 32768;
    unsigned char *buffer = (unsigned char *)malloc(bufSize);
    if (!buffer)
    {
        perror("Failed to allocate memory");
        fclose(file);
        return NULL;
    }

    int bytesRead;
    while ((bytesRead = fread(buffer, 1, bufSize, file)) > 0)
    {
        SHA1_Update(&sha1, buffer, bytesRead);
    }

    SHA1_Final(hash, &sha1);
    fclose(file);
    free(buffer);

    char *outputBuffer = (char *)malloc(SHA_DIGEST_LENGTH * 2 + 1);
    if (!outputBuffer)
    {
        perror("Failed to allocate memory");
        return NULL;
    }

    for (int i = 0; i < SHA_DIGEST_LENGTH; i++)
    {
        sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
    }

    return outputBuffer;
}


/**
 * 版本检查更新
 1. 请求获取最新版本信息  json
 2. 解析json, 得到各个级别的版本号
 3. 比较版本号，如果当前版本不小于最新版本，则不需要更新
 4. 请求下载远程的固件，如果请求失败，则退出
 5. 下载成功后，计算本地文件sha值，得到获取的sha值
 6. 比较本地文件sha值和获取的sha值，如果相同，则更新成功(重启)，否则更新失败
 */
int ota_version_checkUpdate(void){
    //1. 请求获取最新版本信息  json
    char *json =ota_http_getJson(OTA_URL_FILEINFO);
    //2. 解析json, 得到各个级别的版本号
    cJSON *root = cJSON_Parse(json); //创建对象
    int major= cJSON_GetObjectItem(root, "major")->valueint;
    int minor = cJSON_GetObjectItem(root, "minor")->valueint;
    int patch = cJSON_GetObjectItem(root, "patch")->valueint;
    //3.判断是否本地版本号大于等于远程版本号
    if (VERSION_MAJOR > major || (VERSION_MAJOR == major && VERSION_MINOR > minor || (VERSION_MAJOR == major && VERSION_MINOR == minor && VERSION_PATCH >= patch)))
    {
        log_debug("当前是最新版本，不需要更新");
        return 0;
    }
    //4. 请求下载远程的固件，如果请求失败，则退出（执行到这说明需要更新）
    unlink(OTA_LOCAL_FILE_PATH); //先删除旧的本地文件
    int res= ota_http_download(OTA_URL_DOWNLOAD,OTA_LOCAL_FILE_PATH);
    if(res == -1){
        log_debug("下载失败");
        return -1;
    }
    //5. 下载成功后，计算本地文件sha值，得到获取的sha值
    char *local_sha = get_file_sha(OTA_LOCAL_FILE_PATH);
    char *sha_remote = cJSON_GetObjectItem(root, "sha1")->valuestring;
    log_debug("本地sha值: %s, 远程sha值: %s", local_sha, sha_remote);
    //6. 比较本地文件sha值和获取的sha值，如果相同，则更新成功(重启)，否则更新失败
    if(strcmp(local_sha,sha_remote) != 0){

        log_error("更新失败");
        //释放资源
        free(local_sha);
        free(json); 
        cJSON_Delete(root);
        return -1;
    }
    log_debug("更新成功,准备重新系统安装运行新版本");
    //重启系统(必须是root权限,当前不是root，后面开发板运行会是root权限)
    reboot(RB_AUTOBOOT);
    return 0;
    
}

//每天检查一次版本更新
int ota_version_checkUpdataDaliy(void){
    ota_version_checkUpdate();
    log_debug("今天检查完毕，等待一天再执行更新检查");
    sleep(24*60*60);
}

//打印当前本地版本号
void ota_version_getVersion(void){
    log_debug("当前版本号：%d.%d.%d",VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH);
}

